//Translate from selpg.c to selpg.go by huziyuan, 18342026
//Use UTF-8 by Vscode, not ANSI

/*=================================================================

Program name:
	selpg (SELect PaGes)

Purpose:
	Sometimes one needs to extract only a specified range of
pages from an input text file. This program allows the user to do
that.

Author: Vasudev Ram

===================================================================*/

/*================================= includes ======================*/

package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"os/exec"

	"github.com/spf13/pflag"
)

/*================================= types =========================*/

type sp_args struct {
	start_page  int
	end_page    int
	in_filename string
	page_len    int  /* default value, can be overriden by "-l number" on command line */
	page_type   bool /* 'l' for lines-delimited, 'f' for form-feed-delimited */
	/* default is 'l' */
	print_dest string
}

/*================================= globals =======================*/

var progname string                /* program name, for error messages */
const INT_MAX = int(^uint(0) >> 1) //golang需要手动声明INT_MAX

/*================================= main()=== =====================*/

func main() {

	var sa sp_args
	sa.start_page = 1
	sa.end_page = 1
	sa.in_filename = ""
	sa.page_len = 20 //默认20行一页
	sa.page_type = false
	sa.print_dest = ""

	/* save name by which program is invoked, for error messages */
	progname = os.Args[0]

	process_args(len(os.Args), &sa)
	process_input(sa)
}

/*================================= process_args() ================*/

func process_args(ac int, psa *sp_args) {
	//指令格式：selpg -sstart_page -eend_page [-lline | -f ] [-d dstFile] filename
	//使用pflag绑定各参数, psa初始化
	pflag.Usage = usage
	pflag.IntVarP(&psa.start_page, "start_page", "s", 1, "Start page")
	pflag.IntVarP(&psa.end_page, "end_page", "e", 1, "End page")
	pflag.IntVarP(&psa.page_len, "page_len", "l", 20, "Lines per page")
	pflag.BoolVarP(&psa.page_type, "page_type", "f", false, "Page type")
	pflag.StringVarP(&psa.print_dest, "dest", "d", "", "Destination")
	pflag.Parse()

	/* check the command-line arguments for validity */
	if ac < 3 { /* Not enough args, minimum command is "selpg -sstartpage -eend_page"  */
		fmt.Fprintf(os.Stderr, "%s: not enough arguments\n", progname)
		pflag.Usage()
		os.Exit(1)
	}

	/* handle 1st arg - start page */
	temp := os.Args[1]
	if temp[0:2] != "-s" { //待补充，这里确实是[0:2] 原因未知。。
		fmt.Fprintf(os.Stderr, "%s: 1st arg should be -sstart_page\n", progname)
		pflag.Usage()
		os.Exit(2)
	}

	if psa.start_page < 1 || psa.start_page > (INT_MAX-1) {
		fmt.Fprintf(os.Stderr, "%s: invalid start page %d\n", progname, psa.start_page)
		pflag.Usage()
		os.Exit(3)
	}

	/* handle 2nd arg - end page */
	temp = os.Args[2]
	if temp[0:2] != "-e" {
		fmt.Fprintf(os.Stderr, "%s: 2nd arg should be -eend_page\n", progname)
		pflag.Usage()
		os.Exit(4)
	}

	if psa.end_page < 1 || psa.end_page > (INT_MAX-1) || psa.end_page < psa.start_page {
		fmt.Fprintf(os.Stderr, "%s: invalid end page %d\n", progname, psa.end_page)
		pflag.Usage()
		os.Exit(5)
	}

	/* now handle optional args */
	//使用pflag，selpg.c的while+switch可去掉
	if psa.page_len != 5 {
		if psa.page_len < 1 {
			fmt.Fprintf(os.Stderr, "%s: invalid page length %d\n", progname, psa.page_len)
			pflag.Usage()
			os.Exit(6)
		}
	}

	if pflag.NArg() > 0 { /* there is one more arg */
		psa.in_filename = pflag.Arg(0)
		/* check if file exists */
		file, err := os.Open(psa.in_filename)
		if err != nil {
			fmt.Fprintf(os.Stderr, "%s: input file \"%s\" does not exist\n", progname, psa.in_filename)
			os.Exit(7)
		}
		/* check if file is readable */
		file, err = os.OpenFile(psa.in_filename, os.O_RDONLY, 0666)
		if err != nil {
			if os.IsPermission(err) {
				fmt.Fprintf(os.Stderr, "%s: input file \"%s\" exists but cannot be read\n", progname, psa.in_filename)
				os.Exit(8)
			}
		}
		file.Close()
	}

}

/*================================= process_input() ===============*/

func process_input(sa sp_args) {
	var fin *os.File        /* input stream */
	var fout io.WriteCloser /* output stream */
	var c byte              /* to read 1 char */
	var line string
	var line_ctr int /* line counter */
	var page_ctr int /* page counter */
	var err error
	cmd := &exec.Cmd{}

	/* set the input source */
	if sa.in_filename == "" {
		fin = os.Stdin
	} else {
		fin, err = os.Open(sa.in_filename)
		if err != nil {
			fmt.Fprintf(os.Stderr, "%s: could not open input file \"%s\"\n", progname, sa.in_filename)
			os.Exit(9)
		}
	}

	/* set the output destination */
	if sa.print_dest == "" {
		fout = os.Stdout
	} else {
		cmd = exec.Command("cat") //由于没有打印机，使用cat命令测试
		cmd.Stdout, err = os.OpenFile(sa.print_dest, os.O_WRONLY|os.O_TRUNC, 0600)
		if err != nil {
			fmt.Fprintf(os.Stderr, "%s: could not open output file \"%s\"\n", progname, sa.print_dest)
			os.Exit(10)
		}

		fout, err = cmd.StdinPipe()
		if err != nil {
			fmt.Fprintf(os.Stderr, "%s: could not open pipe to \"%s\"\n", progname, sa.print_dest)
			os.Exit(11)
		}
		cmd.Start()
	}

	/* begin one of two main loops based on page type */
	rd := bufio.NewReader(fin)
	if sa.page_type == false {
		line_ctr = 0
		page_ctr = 1
		for true {
			line, err = rd.ReadString('\n')
			if err != nil { /* error or EOF */
				break
			}
			line_ctr++
			if line_ctr > sa.page_len {
				page_ctr++
				line_ctr = 1
			}
			if page_ctr >= sa.start_page && page_ctr <= sa.end_page {
				fmt.Fprintf(fout, "%s", line)
			}
		}
	} else {
		page_ctr = 1
		for true {
			c, err = rd.ReadByte()
			if err != nil { /* error or EOF */
				break
			}
			if c == '\f' {
				page_ctr++
			}
			if page_ctr >= sa.start_page && page_ctr <= sa.end_page {
				fmt.Fprintf(fout, "%c", c)
			}
		}
		fmt.Print("\n") 
	}

	/* end main loop */
	if page_ctr < sa.start_page {
		fmt.Fprintf(os.Stderr, "%s: start_page (%d) greater than total pages (%d), no output written\n", progname, sa.start_page, page_ctr)
	} else if page_ctr < sa.end_page {
		fmt.Fprintf(os.Stderr, "%s: end_page (%d) greater than total pages (%d), less output than expected\n", progname, sa.end_page, page_ctr)
	}

	fin.Close()
	fout.Close()
	fmt.Fprintf(os.Stderr, "%s: done\n", progname)
}

/*================================= usage() =======================*/

func usage() {
	fmt.Fprintf(os.Stderr, "\nUSAGE: %s -sstart_page -eend_page [ -f | -llines_per_page ] [ -ddest ] [ in_filename ]\n", progname)
}

/*================================= EOF ===========================*/
